Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues allow oversized payloads to reach mailbox assertions, break IPC3 userspace builds, schedule uninitialized standalone workqueues, and leave stale messages after proxy teardown.
4 open issues
What changed in this PR
This PR hardens the IPC boundary between user threads and the kernel by separating sensitive IPC state and queuing kernel-owned copies of user messages.
Changes:
- Moves IPC queue, locking, task, and completion state into kernel-only storage for userspace LL builds.
- Adds bounded user-message slots, domain-based ownership, teardown cleanup, and reply-size validation.
- Documents the ownership model and adds standalone userspace queue tests.
| File | Description |
|---|---|
zephyr/test/userspace/test_ipc_msg_queue.c |
Adds message-copy, validation, ownership, and purge tests. |
zephyr/test/CMakeLists.txt |
Registers the new standalone userspace tests. |
zephyr/lib/pm_runtime.c |
Reads IPC task state through the kernel accessor. |
zephyr/Kconfig |
Adds user-message slot count and payload-size settings. |
src/schedule/zephyr_dp_schedule_application.c |
Purges queued messages during domain teardown. |
src/kernel/ipc/ipc4/handler-kernel.c |
Uses kernel-owned IPC state and buffer pointers. |
src/kernel/ipc/ipc-zephyr.c |
Migrates transport and power-management state access. |
src/kernel/idc/zephyr_idc.c |
Uses kernel-owned locking and completion state. |
src/kernel/idc/idc.c |
Dispatches commands through the kernel-held buffer pointer. |
src/ipc/ipc4/handler-user.c |
Bounds forwarded replies and uses the trusted buffer pointer. |
src/ipc/ipc-common.c |
Implements kernel state storage and domain-owned message copies. |
src/include/sof/userspace/README.md |
Documents message ownership and kernel-state isolation. |
src/include/sof/ipc/msg.h |
Declares the domain message-purge API. |
src/include/sof/ipc/common.h |
Defines kernel-only IPC state and its accessor. |
Review effort: Balanced
PR 11439: test resultsRun date: 2026-11-11 13:41 UTC Tested commit: a891d2217c8e33a0b332952d3a52b78aca8ec075 |
With CONFIG_SOF_USERSPACE_LL, struct ipc is in a partition that the LL and IPC user threads can write. The send queue head, its spinlock, the D3 preparation flag and the completion task mask are only used by kernel code, but a user thread could link a forged message into the queue, corrupt the lock or make the kernel skip or delay the host reply. Move these fields to a kernel-only struct ipc_kstate when CONFIG_SOF_USERSPACE_LL is set, and access them through ipc_ks(). The field names are the same as in struct ipc, which keeps them when CONFIG_SOF_USERSPACE_LL is not set, so other builds are unchanged. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
The IPC processing task is run by the kernel EDF or TWB scheduler, which calls through its ops and reads its scheduling state. With CONFIG_SOF_USERSPACE_LL it is embedded in struct ipc, which LL and IPC user threads can write, so a user thread could redirect kernel execution through it. Move it to struct ipc_kstate when CONFIG_SOF_USERSPACE_LL is set. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
With CONFIG_SOF_USERSPACE_LL, ipc->comp_data is in user-writable struct ipc. Kernel code builds replies from this buffer and passes it to ipc_msg_send(), which copies it into the reply message when the two pointers differ, so a user thread could redirect a kernel memory copy or make the kernel send other memory to the host. The reply to a forwarded LARGE_CONFIG_GET also took its payload pointer and size from struct ipc_user, which user threads can write. Keep a copy of the buffer pointer in struct ipc_kstate and use it in the kernel paths. For forwarded LARGE_CONFIG_GET replies, always reply from this buffer, where the IPC user thread writes the reply, and check the size read from struct ipc_user once against the message and mailbox limits. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
The ipc_msg_send system call linked the struct ipc_msg of the calling user thread into the kernel send queue. The send work queue then called msg->callback in supervisor mode and read the header, size and payload pointer from user memory, all of which the thread can change at any time. A DP module thread could use this to run any kernel code or to corrupt the kernel queue. The validator also faulted a thread that sent a message again while it was still queued, which modules such as tdfb and mfcc do. Copy the header and payload once into a kernel-owned slot instead and queue the slot. The slot is found again by the address of the user message and the memory domain of the sender, neither of which is dereferenced. Sending a message that is still queued updates the queued copy, which matches the in-place update done for kernel messages. ipc_msg_list_remove now removes the matching slot, and does nothing if there is none. User messages are limited to CONFIG_SOF_IPC_USER_MSG_MAX_SIZE bytes, and a message is dropped if all CONFIG_SOF_IPC_USER_MSG_SLOTS slots are in use. Add ipc_user_msg_purge() for dropping the queued messages of a memory domain before it is reused. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
DP memory domains and userspace proxy domains are reused for new modules. Drop the messages that the threads of a freed domain still have queued, so that they are not sent on behalf of a module that no longer exists, and not matched to messages of the next module using the domain. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Check that the kernel queues its own copy of a message sent by a user thread: changing the user message after sending it, or forging its list pointers and callback, has no effect on the queued message, a message sent again while queued is updated in place, and messages are removed only by their own memory domain. Also check that oversized messages and kernel payload addresses are rejected, and that the number of queued messages is limited to the number of slots. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Describe how messages sent by user threads are copied to kernel-owned slots, how they are matched for re-send and removal, and that the IPC send queue state is kernel-only with CONFIG_SOF_USERSPACE_LL. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
a359ac2 to
a891d22
Compare
|
V2 pushed:
|
There was a problem hiding this comment.
🟡 Changes recommended
The 64-byte IPC3 default rejects legitimate 76-byte stream-position notifications, causing host-position and XRUN sends to fault the audio thread instead of notifying the host when userspace LL is enabled.
1 open issue
4 resolved since last review
Review effort: Balanced
|
|
||
| config SOF_IPC_USER_MSG_MAX_SIZE | ||
| int "Maximum payload size of an IPC message from a user thread" | ||
| default 64 |


A series to refactor the IPC implementation to remove all instances where user-writable data is used in kernel.